#pragma once
#include <iostream>
#include <pthread.h>
#include <queue>
#include <stdlib.h>
#include <unistd.h>
using namespace std;

namespace my_threadpool
{
    const int default_num = 5;
    template <class T>
    class ThreadPool
    {
    private:
        int _num;             //线程的数量
        queue<T> _task_queue; //创建任务队列，用来存放产生的任务供线程处理，是个临界资源

        pthread_mutex_t _mtx;
        pthread_cond_t _cond;
        static ThreadPool<T> *ins;

        ThreadPool(int num = default_num) : _num(num)
        {
            pthread_mutex_init(&_mtx, nullptr);
            pthread_cond_init(&_cond, nullptr);
        }
        ThreadPool(const ThreadPool<T> &tp) = delete;
        ThreadPool &operator=(ThreadPool<T> tp) = delete;

    public:
        ~ThreadPool()
        {
            pthread_mutex_destroy(&_mtx);
            pthread_cond_destroy(&_cond);
        }
        void PopTask(T *out)
        {
            *out = _task_queue.front();
            _task_queue.pop();
        }
        void Lock()
        {
            pthread_mutex_lock(&_mtx);
        }
        void UnLock()
        {
            pthread_mutex_unlock(&_mtx);
        }
        void WakeUp()
        {
            pthread_cond_signal(&_cond);
        }
        void Wait()
        {
            pthread_cond_wait(&_cond, &_mtx);
        }
        bool IsEmpty()
        {
            return _task_queue.empty();
        }

    public:
        //通过该静态函数来生成一个对象
        static ThreadPool<T> *GetInstance()
        {
            //这把锁是用来防止多个线程生成多个类对象的
            //因为线程准备生成对象前也可能被切走
            static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

            if (ins == nullptr)//双判断减少锁的申请，提高获取单例的效率
            {
                //后面的线程每次进来都需要申请锁，影响效率，可以加一个if，双判断
                pthread_mutex_lock(&lock);
                //这里我个人感觉可以把锁放到下面的这个循环里面
                if (ins == nullptr)
                {
                    ins = new ThreadPool<T>();
                    ins->InitPthreadPool();
                    std::cout << "首次加载对象" << std::endl;
                }
                pthread_mutex_unlock(&lock);
            }
            return ins;
        }
        // 在类中要让线程执行类内成员方法，是不可行的！
        // 必须让线程执行静态方法
        static void *Routine(void *args) //但是为了调用类内封装的成员函数，我们还需this指针
        {
            pthread_detach(pthread_self()); //将自己分离出去
            ThreadPool<T> *tp = (ThreadPool<T> *)args;
            while (true)
            {
                tp->Lock();
                while (tp->IsEmpty())
                {
                    tp->Wait();
                }
                T t;
                tp->PopTask(&t);
                tp->UnLock();
                t.Run();
            }
        }
        void InitPthreadPool()
        {
            pthread_t tid;
            for (int i = 0; i < _num; i++)
            {
                pthread_create(&tid, nullptr, Routine, (void *)this); //传this指针给静态成员函数
            }
        }
        void Push(const T &data)
        {
            Lock();
            _task_queue.push(data);
            UnLock();
            WakeUp();
        }
    };

    template <class T>
    ThreadPool<T> *ThreadPool<T>::ins = nullptr;

}